﻿// 
// PanelManager.cs
//  
// Author:
//       Tomas Vondracek <tomas.vondracek@gmail.com>
// 
// Copyright (c) 2010 Tomas Vondracek
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Common.Interfaces;
using System.Diagnostics;
using System.Windows.Forms;
using System.Drawing;

namespace Common.Gui.GuiWinFormsLib.Controls
{
	internal class DockingElement
	{
		public IDockablePanel panel;
		public ISwitcher switcherButton;

		public DockingElement(IDockablePanel panel, ISwitcher switcher)
		{
			Debug.Assert(panel != null, "panel is null.");
			Debug.Assert(switcher != null, "switcher is null.");

			this.panel = panel;
			this.switcherButton = switcher;
		}
	}

	/// <summary>
	/// for each container is one PanelManager - takes care about
	/// panel switching logic
	/// </summary>
	internal class PanelManager<T>
		where T : ISwitcher
	{
		protected Dictionary<string, DockingElement> dockablePanels;
		protected DockingContainer container;

		string lastAcivate;
		string currentActive;

		internal PanelManager(DockingContainer container)
		{
			Debug.Assert(container != null, "container is null.");
			this.container = container;

			this.dockablePanels = new Dictionary<string, DockingElement>();
		}

		/// <summary>
		/// register panel to the manager,
		/// will also create the switcher button
		/// </summary>
		/// <param name="panel">panel to add</param>
		/// <remarks>first added panel will be considered as active</remarks>
		internal void RegisterPanel(IDockablePanel panel)
		{
			if (panel == null)
				throw new ArgumentNullException("panel", "panel is null.");

			ISwitcher switcherButton = CreatePanelSwitcherButton(panel);

			// TODO - title isn't id
			if (!this.dockablePanels.ContainsKey(panel.TitleText))
			{
				this.dockablePanels.Add(panel.TitleText, new DockingElement(panel, switcherButton));
				
				// first added panel will be active
				if (this.PanelCount == 1)
					this.currentActive = panel.TitleText;


				// if panel can be floating we need to know
				if (panel is FloatingDockablePanel)
				{
					(panel as FloatingDockablePanel).DockablePanelBecomeFloating += new EventHandler(PanelManager_DockablePanelBecomeFloating);
					(panel as FloatingDockablePanel).DockablePanelReturnFromFloating += new EventHandler(PanelManager_DockablePanelReturnFromFloating);
				}
			}
		}

		void PanelManager_DockablePanelReturnFromFloating(object sender, EventArgs e)
		{
			if (sender != null && sender is IDockablePanel)
			{
				var id = (sender as IDockablePanel).TitleText;
				var element = this.dockablePanels[id];
				SwitchToPanel(element.switcherButton, element.panel);
			}
		}

		void PanelManager_DockablePanelBecomeFloating(object sender, EventArgs e)
		{
			var panel = sender as DockablePanel;

			if (panel != null && panel.TitleText == this.lastAcivate)
			{
				this.lastAcivate = null;
			}
			SwitchToLastActivePanel();
		}

		private void SwitchToPanel(ISwitcher btn, IDockablePanel panel)
		{
			SwitchToPanel(btn, panel, true);
		}

		/// <summary>
		/// make given panel active
		/// </summary>
		/// <param name="btn">switcher that belongs to the panel</param>
		/// <param name="panel">panel to switch to</param>
		/// <param name="hideLast">true to hide last active panel</param>
		private void SwitchToPanel(ISwitcher btn, IDockablePanel panel, bool hideLast)
		{
			// show the panel
			btn.Activated = true;
			panel.CurrentState = DockablePanelState.Normal;

			if (hideLast)
			{
				// will hide the last active panel if it is visible - in case of only one panel inside, will skip hidding
				var element = this.dockablePanels[currentActive];
				if (element.panel.IsVisible && this.dockablePanels.Count > 1 && this.currentActive != this.lastAcivate)
				{
					element.panel.CurrentState = DockablePanelState.Hidden;
					element.switcherButton.Activated = false;
				}
			}
			this.lastAcivate = this.currentActive;
			this.currentActive = panel.TitleText;
		}

		internal void SwitchToLastActivePanel()
		{
			if (!string.IsNullOrEmpty(this.lastAcivate) && this.dockablePanels.ContainsKey(this.lastAcivate))
			{
				DockingElement element = this.dockablePanels[this.lastAcivate];
				DockingElement currentElement = this.dockablePanels[this.currentActive];

				// if another panel became flaoting - switch to the last active, 
				// but do not hide the floating one
				var hide = currentElement.panel.CurrentState != DockablePanelState.Floating;
				SwitchToPanel(element.switcherButton, element.panel, hide);
			}
		}

		/// <summary>
		/// create button to represent the dockablePanel and to control it ... hiding, showing
		/// </summary>
		/// <param name="panel">a DockablePanel, which will the button belong to</param>
		protected ISwitcher CreatePanelSwitcherButton(IDockablePanel panel)
		{
			if (panel == null)
				throw new ArgumentNullException("panel", "panel is null.");

			var controlPanelHeight = this.container.ControlBarHeight;

			// create ISwitcher control
			SwitcherButton switcherButton = new SwitcherButton();
			switcherButton.TitleText = panel.TitleText;
			switcherButton.Height = controlPanelHeight - controlPanelHeight / 4;
			switcherButton.Length = 100;
			switcherButton.Location = new Point((this.dockablePanels.Count) * switcherButton.Width, 0);
			switcherButton.Padding = new Padding(5);
			switcherButton.Margin = new Padding(5);
			switcherButton.Click += new EventHandler(switcherButton_Click);
			switcherButton.DefaultHeight = controlPanelHeight - controlPanelHeight / 4;

			// add it to the container
			this.container.AddSwitcherButton(switcherButton);
			// attach the panel
			switcherButton.AttachedPanel = panel;

			//update button text when needed
			panel.DockablePanelTitleTextChanged += delegate
			{
				try
				{
					switcherButton.TitleText = panel.TitleText;
				}
				catch (Exception ex)
				{
					Logging.Logger.AddException(ex);
				}
			};

			return switcherButton;
		}

		/// <summary>
		/// handle switcher button click
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		void switcherButton_Click(object sender, EventArgs e)
		{
			Debug.Assert(sender != null, "sender is null.");

			try
			{
				ISwitcher btn = sender as ISwitcher;
				if (btn != null && btn.AttachedPanel is DockablePanel)
				{
					DockablePanel panel = btn.AttachedPanel as DockablePanel;
					UpdatePanels(panel);
				}
				else
					Logging.Logger.AddWarning("PanelManager: restore panel button has not valid dockable panel as its attached panel!");
			}
			catch (Exception ex)
			{
				Logging.Logger.AddException(ex);
			}
		}

		private void UpdatePanels(DockablePanel panel)
		{
			ISwitcher btn = this.dockablePanels[panel.TitleText].switcherButton;

			if (panel.CurrentState == DockablePanelState.Floating)
			{
				(panel as FloatingDockablePanel).BringFloatingFormToFront();
				Logging.Logger.AddInfo("PanelManager - bringing the floating form to the front");
			}
			else if (panel.CurrentState == DockablePanelState.Hidden)
			{
				SwitchToPanel(btn, panel);

				Logging.Logger.AddInfo("PanelManager - showing the hidden panel");
			}
			else if (panel.CurrentState == DockablePanelState.Normal)
			{
				// if there is only one panel inside and EnableEmptyContainer is false
				// do not hide the panel
				if (this.dockablePanels.Count == 1 && this.container.EnableEmptyContainer)
				{
					// hide the panel if the state is normal
					panel.CurrentState = DockablePanelState.Hidden;
					this.lastAcivate = panel.TitleText;
					Logging.Logger.AddInfo("PanelManager - hiding the dockable panel");
				}
				else
				{
					SwitchToLastActivePanel();
				}
			}
		}

		/// <summary>
		/// check if all registered panels are minimized or hidden
		/// </summary>
		/// <returns></returns>
		internal bool AreAllPanelsMinimized
		{
			get
			{
				return AreAllPanelsInState(DockablePanelState.Hidden);
			}
		}

		internal bool AreAllPanelsOutside
		{
			get
			{
				var visibleCount = from v in this.dockablePanels.Values
								where v.panel.CurrentState != DockablePanelState.Hidden &&
										v.panel.CurrentState != DockablePanelState.Floating
								select v;
				return visibleCount.Count() == 0;
			}
		}

		/// <summary>
		/// check if all registered panels are minimized or hidden
		/// </summary>
		/// <returns></returns>	
		internal bool AreAllPanelsFloating
		{
			get
			{
				return AreAllPanelsInState(DockablePanelState.Floating);
			}
		}

		/// <summary>
		/// detect wether all registered panels are in given DockablePanelState
		/// </summary>
		/// <param name="state"></param>
		/// <returns></returns>
		private bool AreAllPanelsInState(DockablePanelState state)
		{
			bool allInState = true;
			foreach (var element in this.dockablePanels)
			{
				if (element.Value.panel.CurrentState != state)
				{
					allInState = false;
					break;
				}
			}

			return allInState;
		}

		internal int PanelCount
		{
			get { return this.dockablePanels.Count; }
		}

		/// <summary>
		/// perform ResumeLayout on all registered dockable panels 
		/// </summary>
		/// <param name="performLayout">
		/// A <see cref="System.Boolean"/>
		/// </param>
		internal void ResumeLayout(bool performLayout)
		{
			foreach (var panel in this.dockablePanels)
			{
				(panel.Value.panel as Control).ResumeLayout(performLayout);
			}
		}
		
		/// <summary>
		/// perform SuspendLayout on all registered panels 
		/// </summary>
		internal void SuspendLayout()
		{
			foreach (var panel in this.dockablePanels)
			{
				(panel.Value.panel as Control).SuspendLayout();
			}
		}
		
		internal IEnumerable<IDockablePanel> GetRegisteredPanels()
		{
			foreach (var item in dockablePanels.Values)
			{
				yield return item.panel;
			}
		}
	}
}
